package com.jprocms.module.cms.controller.portal.api;

import com.jpro.framework.common.pojo.CommonResult;
import com.jpro.framework.common.pojo.PageResult;
import com.jpro.framework.common.util.object.BeanUtils;
import com.jpro.framework.ip.core.Area;
import com.jpro.framework.ip.core.utils.AreaUtils;
import com.jpro.framework.ip.core.utils.IPUtils;
import com.jpro.framework.security.core.util.SecurityFrameworkUtils;
import com.jprocms.module.cms.api.dto.SiteConfigCommentDto;
import com.jprocms.module.cms.controller.admin.comment.vo.CommentListReqVO;
import com.jprocms.module.cms.controller.admin.comment.vo.CommentRespVO;
import com.jprocms.module.cms.controller.admin.comment.vo.CommentSaveReqVO;
import com.jprocms.module.cms.controller.admin.comment.vo.CommentSubmitDTO;
import com.jprocms.module.cms.controller.directive.CommentListDirective;
import com.jprocms.module.cms.dal.dataobject.comment.CommentDO;
import com.jprocms.module.cms.dal.dataobject.content.ContentCountDO;
import com.jprocms.module.cms.dal.dataobject.content.ContentDO;
import com.jprocms.module.cms.dal.dataobject.site.SiteDO;
import com.jprocms.module.cms.enums.EnumCommentStatus;
import com.jprocms.module.cms.enums.EnumIntCommonStatus;
import com.jprocms.module.cms.framework.cache.CacheService;
import com.jprocms.module.cms.service.comment.CommentService;
import com.jprocms.module.cms.service.content.ContentCountService;
import com.jprocms.module.cms.service.content.ContentService;
import com.jprocms.module.cms.util.CmsContextUtil;
import com.jprocms.module.cms.util.QueryUtils;
import com.jprocms.module.cms.util.RequestUtils;
import com.jprocms.module.cms.web.Directives;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;

import static com.jpro.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.jprocms.module.cms.constant.CmsSysConstants.FRONT_API;
import static com.jprocms.module.cms.dal.redis.RedisKeyConstants.IP_COMMENT_LAST_TIME;
import static com.jprocms.module.cms.enums.ErrorCodeConstants.*;

/**
 * 评论前台 接口
 *
 * @author jprocms
 */
@Tag(name = "CommentController", description = "评论接口")
@RestController
@RequestMapping({FRONT_API + "/comment"})
public class CommentApiController {
    private final CommentService service;
    private final ContentService contentService;
    private CacheService cacheService;
    private final ContentCountService contentCountService;

    public CommentApiController(CommentService service, ContentService contentService, CacheService cacheService, ContentCountService contentCountService) {
        this.service = service;
        this.contentService = contentService;
        this.cacheService = cacheService;
        this.contentCountService = contentCountService;
    }

    @Operation(summary = "获取评论列表（CommentList标签）")
    @Parameter(in = ParameterIn.QUERY, name = "contentId", description = "内容ID",
            schema = @Schema(type = "long", format = "int32"))
    @Parameter(in = ParameterIn.QUERY, name = "offset", description = "从第几条数据开始获取。默认为0，即从第一条开始获取",
            schema = @Schema(type = "integer", format = "int32"))
    @Parameter(in = ParameterIn.QUERY, name = "limit", description = "共获取多少条数据。最大不能超过1000",
            schema = @Schema(type = "integer", format = "int32"))
    @GetMapping
    public CommonResult<List<CommentRespVO>> list(HttpServletRequest request) {
        return query(request, (args, params) -> {
            int offset = Directives.getOffset(params);
            int count = Directives.getCount(params);
            args.setOffset(offset);
            args.setCount(count);
            List<CommentDO> commentList = service.getCommentList(args);
            return CommonResult.success(BeanUtils.toBean(commentList, CommentRespVO.class));
        });
    }

    @Operation(summary = "获取评论分页（CommentPage标签）")
    @Parameter(in = ParameterIn.QUERY, name = "contentId", description = "内容ID",
            schema = @Schema(type = "long", format = "int32"))
    @Parameter(in = ParameterIn.QUERY, name = "page", description = "第几页",
            schema = @Schema(type = "integer", format = "int32"))
    @Parameter(in = ParameterIn.QUERY, name = "pageSize", description = "每页多少条数据。最大不能超过1000",
            schema = @Schema(type = "integer", format = "int32"))
    @GetMapping("/page")
    public CommonResult<PageResult<CommentRespVO>> page(HttpServletRequest request) {
        return query(request, (args, params) -> {
            int page = Directives.getPage(params);
            int pageSize = Directives.getPageSize(params);
            args.setPageNo(page);
            args.setPageSize(pageSize);
            PageResult<CommentDO> commentPage = service.getCommentPage(args);
            return CommonResult.success(BeanUtils.toBean(commentPage, CommentRespVO.class));
        });
    }


    private <T> T query(HttpServletRequest request, BiFunction<CommentListReqVO, Map<String, String>, T> handle) {
        SiteDO site = CmsContextUtil.getSite();
        CommentListReqVO args = new CommentListReqVO();
        Map<String, String> params = QueryUtils.getParams(request.getQueryString());
        CommentListDirective.assemble(args, params, site.getId());
        return handle.apply(args, params);
    }

    @Operation(summary = "获取评论对象（Vote标签）")
    @ApiResponses(value = {@ApiResponse(description = "评论对象")})
    @GetMapping("/{id:[\\d]+}")
    public CommonResult<CommentRespVO> getDetail(@Parameter(description = "评论ID") @PathVariable Long id) {
        CommentDO comment = service.getComment(id);
        validateComment(comment);
        return CommonResult.success(BeanUtils.toBean(comment, CommentRespVO.class));
    }

    public static void validateComment(CommentDO commentDO) {
        if (commentDO == null) {
            throw exception(COMMENT_NOT_EXISTS);
        }
        if (!EnumCommentStatus.CHECK_PASS.getStatus().equals(commentDO.getStatus())) {
            throw exception(COMMENT_NOT_EXISTS);
        }
    }

    @Operation(summary = "评论")
    @ApiResponse(description = "msg: 提示信息。code: 状态码。0，成功；其他失败，失败消息在msg")
    @PostMapping
    public CommonResult<Boolean> comment(@RequestBody @Valid CommentSubmitDTO params, HttpServletRequest request) {
        final SiteDO site = CmsContextUtil.getSite();
        final SiteConfigCommentDto commentConfig = site.getSiteConfig().getComment();
        if (EnumIntCommonStatus.CLOSE.getStatus().equals(commentConfig.getCommentStatus())) {
            throw exception(COMMENT_NOT_ENABLE);
        }
        //未登录不允许评论
        Long currLoginUserId = SecurityFrameworkUtils.getLoginUserId();
        if (commentConfig.getCommentNeedLogin() && currLoginUserId == null) {
            throw exception(COMMENT_NOT_LOGIN);
        }
        final ContentDO content = contentService.getContent(params.getContentId());
        if (content == null) {
            throw exception(CONTENT_NOT_EXISTS);
        }
        String ip = RequestUtils.getRemoteAddr(request);
        //评论周期大于0 需要检查最后评论时间
        if (commentConfig.getCommentInterval() > 0) {
            final Boolean hasLastTimeKey = cacheService.hasKey(IP_COMMENT_LAST_TIME + ip);
            //是否过于频繁
            if (hasLastTimeKey) {
                throw exception(COMMENT_TOO_FAST);
            }
            //通过更新redis时间
            cacheService.setCacheObject(IP_COMMENT_LAST_TIME + ip, System.currentTimeMillis(), commentConfig.getCommentInterval().longValue(), TimeUnit.SECONDS);
        }
        CommentSaveReqVO commentSaveReqVO = new CommentSaveReqVO();
        commentSaveReqVO.setSiteId(site.getId());
        commentSaveReqVO.setText(params.getText());
        commentSaveReqVO.setContentId(params.getContentId());
        if (commentConfig.getCommentAudit()) {
            commentSaveReqVO.setStatus(EnumIntCommonStatus.CLOSE.getStatus());
        } else {
            commentSaveReqVO.setStatus(EnumIntCommonStatus.OPEN.getStatus());
            ContentCountDO count = contentCountService.getById(params.getContentId());
            int comments = count.getComments() + 1;
            count.setComments(comments);
            contentCountService.updateById(count);
        }
        commentSaveReqVO.setCreateUserIp(ip);
        Long userId = Optional.ofNullable(SecurityFrameworkUtils.getLoginUserId()).orElse(null);
        commentSaveReqVO.setUserId(userId);
        Area area = IPUtils.getArea(ip);
        if (area != null) {
            area = IPUtils.getCity(area);
            final String areaStr = AreaUtils.format(area.getId());
            commentSaveReqVO.setUserCity(areaStr);
        }
        service.createComment(commentSaveReqVO);
        return CommonResult.success(true);
    }
}
